#include "part2d.h"

#include "script/scripthelper.h"
#include "util/log.h"		    // log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		    // Needs to come in early

#include "num/rand.h"
#include "math/math_core.h"

#include "graphics/graphics.h"
#include "graphics/graphics_util.h"
#include "script/write_lua.h"
#ifdef _ENABLE_LUABIND
#include <luabind/luabind.hpp>
#endif //_ENABLE_LUABIND

#include <math.h>

#include <cassert>
using namespace part2d;
using namespace LuaPlus;
using namespace script;




Part2D::Part2D() 
{

}

Part2D::~Part2D()
{
	for (unsigned int i=0; i<m_Systems.size(); i++)
		delete m_Systems[i];
}

bool Part2D::Save(const std::string& File)
{
	m_File = File;
	script::WriteLua	WL;

	if (!WL.Open(File)) return false;

	WL.StartTable("Part2D");
		for (unsigned int i=0; i<m_Systems.size(); i++)
			m_Systems[i]->Save(WL);
	WL.CloseTable(false);
	return true;
}

bool Part2D::Load(const std::string& File)
{
	LuaState* pConfig = script::ScriptHelper::CreateLuaState();
    if (!ScriptHelper::DoFile(*pConfig,  File))
    {
        _LOG(MSG_ERROR, "\t Error loading Particle system object " <<  File);
		LuaState::Destroy( pConfig );
		return false;
    } else
    {  
		m_File = File;
        LuaObject       Data = ScriptHelper::GetObj(*pConfig, "Part2D");
        if (Data.IsTable())
        {
			for (int i=0; i<Data.GetTableCount(); i++)
			{
				LuaObject       System = Data.GetByIndex(i+1);
				if (System.IsTable())
				{
					PartGroup* pNew = new PartGroup(this);
					if (pNew->Load(System))
					{
						m_Systems.push_back(pNew);
					} else
					{
						delete pNew;
					}
				}
			}
		}
	}

	LuaState::Destroy( pConfig );
	return true;
}


PartGroup*	Part2D::CreateGroup()
{
	// creates a new group and populates it with 
	PartGroup* pNew = new PartGroup(this);
	m_Systems.push_back(pNew);
	if (m_Systems.size() > 5)
	{
		_LOG(MSG_ERROR, "You're creating a VERY VERY big particle system. Are you sure you know what you're doing?");
	}

	pNew->SetMaxC(100);
//	pNew->SetImage("effects/test.png");
//		unsigned int i = m_Systems.size();
	return pNew;
}

void	Part2D::SetSizeScale(float SizeScale)
{
	for (unsigned int i=0; i<m_Systems.size(); i++)
		m_Systems[i]->SetSizeScale(SizeScale);
}

bool Part2D::Render(const math::Vec2& Offset,float Scale, const math::Vec2& Min, const math::Vec2& Max)
{
	for (unsigned int i=0; i<m_Systems.size(); i++)
		m_Systems[i]->Render(Offset, Scale, Min, Max);
	return true;
}

bool	Part2D::TickCreation(float dt, const math::Vec2& Pos, const math::Vec2& Size)
{
	bool bRet = false;

	for (unsigned int i=0; i<m_Systems.size(); i++)
	{
		if (!m_Systems[i]->IsSecondary())
		{
			if (m_Systems[i]->TickCreation(dt, Pos, Size))
				bRet = true;
		}
	}
	return bRet;

}

void Part2D::Clear()
{
	for (unsigned int i=0; i<m_Systems.size(); i++)
	{
		m_Systems[i]->ClearParticles();
	}
}

bool Part2D::Tick(float dt)
{
	if (m_Systems.empty()) return false;
	for (unsigned int i=0; i<m_Systems.size(); i++)
		m_Systems[i]->Tick(dt);
	return true;
}

void		Part2D::SetDeathParticleCB(PartGroup::ParticleCallback pCB, void *pData)
{
	for (unsigned int i=0; i<m_Systems.size(); i++)
		m_Systems[i]->SetParticleCB(PartGroup::PC_DEAD, pCB, pData);
}
Particle2D*	  Part2D::TriggerParticle(const math::Vec2& ScreenPos)
{
	if (m_Systems.empty()) return NULL;
	return m_Systems[0]->TriggerParticle(ScreenPos);
}

void		Part2D::TriggerParticles(const math::Vec2& ScreenPos)
{
	for (unsigned int i=0; i<m_Systems.size(); i++)
	{
		if (!m_Systems[i]->IsSecondary())
			m_Systems[i]->TriggerParticle(ScreenPos);
	}
}
void		Part2D::TriggerParticles(const math::Vec2& ScreenPos, const math::Vec2& InitialDir)
{
	for (unsigned int i=0; i<m_Systems.size(); i++)
	{
		if (!m_Systems[i]->IsSecondary())
			m_Systems[i]->TriggerParticle(ScreenPos, InitialDir);
	}
}

void		Part2D::TriggerParticlesAbs(const math::Vec2& ScreenPos, const math::Vec2& InitialDir)
{
	for (unsigned int i=0; i<m_Systems.size(); i++)
	{
		if (!m_Systems[i]->IsSecondary())
			m_Systems[i]->TriggerParticleAbs(ScreenPos, InitialDir);
	}
}

void	  Part2D::TriggerParticlesOnLine(const math::Vec2& From, const math::Vec2& To)
{
	num::RAND32& Rnd = num::RAND32::Instance();
	math::Vec2	Delta(To-From);
	for (unsigned int i=0; i<m_Systems.size(); i++)
	{
		if (!m_Systems[i]->IsSecondary())
		{
			float t = Rnd.RandFloat(0,1);
			m_Systems[i]->TriggerParticle(From + (Delta*t));
		}
	}
}



void		Part2D::TriggerParticleSet(int Count, const math::Vec2& ScreenPos)
{
	for (int j=0; j<Count; j++)
	{
		TriggerParticles(ScreenPos);
	}
}


void		Part2D::TriggerParticleSet(int Count, const math::Vec2& ScreenPos, const math::Vec2& InitialDir)
{
	for (int j=0; j<Count; j++)
	{
		TriggerParticles(ScreenPos, InitialDir);
	}
}


void	  Part2D::TriggerParticlesOnCircle(const math::Vec2& Pos, float Radius, bool CorrectAspectRatio)
{
	num::RAND32& Rnd = num::RAND32::Instance();
	math::Vec2 TriggerPos;
	float deg;

	for (unsigned int i=0; i<m_Systems.size(); i++)
	{
		if (!m_Systems[i]->IsSecondary())
		{
			deg = Rnd.RandFloat(0.0f, MATH_2PI);
			TriggerPos.x = Radius * sin(deg);
			TriggerPos.y = Radius * cos(deg);
			if (CorrectAspectRatio)
				TriggerPos.y *= GraphicsUtil::W2H;

			m_Systems[i]->TriggerParticle(TriggerPos);
		}
	}
}

Particle2D*	 Part2D::TriggerParticle(const math::Vec2& ScreenPos, const math::Vec2& InitialDir)
{
	if (m_Systems.empty()) return NULL;
	return m_Systems[0]->TriggerParticle(ScreenPos, InitialDir);
}

Particle2D*	 Part2D::TriggerParticleAbs(const math::Vec2& ScreenPos, const math::Vec2& InitialDir)
{
	if (m_Systems.empty()) return NULL;
	return m_Systems[0]->TriggerParticleAbs(ScreenPos, InitialDir);
}

PartGroup*	Part2D::GetGroup(unsigned int i)
{
	if (i < m_Systems.size()) return m_Systems[i];
	return NULL;
}
void		Part2D::DeleteGroup(unsigned int i)
{
	if (i < m_Systems.size()) {
		delete m_Systems[i];
		m_Systems.erase(m_Systems.begin() + i);
	}
}

void	Part2D::DeleteGroup(PartGroup* pGroup)
{
	for (unsigned int i=0; i<m_Systems.size(); i++)
	{
		if (m_Systems[i]==pGroup)
		{
			delete m_Systems[i];
			m_Systems.erase(m_Systems.begin() + i);
			return;
		}
	}
}


void	Part2D::ResetGeom()
{
	for (unsigned int i=0; i<m_Systems.size(); i++)
		m_Systems[i]->GetCollision()->Reset();
}
void	Part2D::AddGeom(const math::Vec2& Pos, float r)
{
	for (unsigned int i=0; i<m_Systems.size(); i++)
		m_Systems[i]->GetCollision()->AddGeom(Pos, r);
}

Part2D*	Part2D::Clone(Part2D* pClone)
{
	unsigned int i;
	Part2D* pNew;

	if (pClone)
	{
		pNew = pClone;
		for (i = 0; i < pNew->m_Systems.size(); i++)
			UTIL_SAFE_DELETE(pNew->m_Systems[i]);
		pNew->m_Systems.clear();
	}
	else
		pNew = new Part2D();

	pNew->m_File = m_File;

	for (unsigned int i = 0; i < m_Systems.size(); i++)
	{
		if (PartGroup* pGroup = new PartGroup(pNew)) {
			m_Systems[i]->Clone(pGroup);
			pNew->m_Systems.push_back(pGroup);
		}
	}

	return pNew;
}

bool Part2D::IsActive()
{
	for (int i = 0; i < (int)m_Systems.size(); i++)
	{
		if (m_Systems[i]->IsActive() && (m_Systems[i]->GetPartC() > 0))
			return true;
	}
	return false;
}

bool	Part2D::IsInside(const math::Vec2& Pos)
{
	for (int i = 0; i < (int)m_Systems.size(); i++)
	{
		if (m_Systems[i]->IsInside(Pos))
			return true;
	}
	return false;
}


void Part2D::Register(LuaPlus::LuaState* _pScript)
{
 #ifdef _ENABLE_LUABIND
    lua_State * L = _pScript->GetCState ( );

	PartGroup::Register(_pScript);

	luabind::module(L)
    [

    luabind::class_< Part2D >( "Part2D" )
		.def( "Load",				&Part2D::Load)
		.def( "Save",				&Part2D::Save)
        .def( "GetGroup",			&Part2D::GetGroup)
        .def( "GroupC",				&Part2D::GroupC)
        .def( "CreateGroup",		&Part2D::CreateGroup)
//		.def( "TriggerParticleSet",		 TriggerParticleSet)
	];
#endif // _ENABLE_LUABIND	
}
